package common.service.base.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import common.service.base.annotation.ApiRequestLimit;
import common.service.base.annotation.MobileRequestParam;
import common.service.base.common.MobileSession;
import common.service.base.common.ReqHeader;
import common.service.base.common.ReqVo;
import common.service.base.common.SessionInfo;
import common.service.base.exception.MobileException;
import common.service.base.handler.ThreadLocalHandler;
import common.service.contants.MobileConfig;
import common.service.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @ClassName ApiRequestLimitInterceptor
 * @Description
 * @Author lixin
 * @Date 2020/02/10 下午6:11
 * Version 0.0.1
 **/
@Slf4j
public class ApiRequestLimitInterceptor extends HandlerInterceptorAdapter {

    private static  long countValue = 30;

    private static  long timeValue = 60;

    private static boolean flag =true;

    private static final String REQUEST_LIMIT_KEY ="REQUEST_LIMIT_KEY:";

    private static final String REQUEST_UNDERLINE =":";
    private static long initCountValue=1;
    private Logger logger = LoggerFactory.getLogger(ApiRequestLimitInterceptor.class);

    @Autowired
    private MobileSession mobileSession;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	if(!(handler instanceof HandlerMethod)){
        	log.info("request 拒绝: param method:{},URL:{}",request.getMethod(),request.getRequestURL());
            return  true;
        }
        String cdid =null;
        Long userId =null;
        String key =null;
        StringBuilder sb = new StringBuilder();
        Map<String,Object> map =null;
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        ApiRequestLimit annotation = method.getAnnotation(ApiRequestLimit.class);
        Annotation[][] parameterAnnotations =
                method.getParameterAnnotations();
        boolean flag =false;
        for (Annotation[] an1 : parameterAnnotations) {
            for (Annotation an2 : an1) {
                if(MobileRequestParam.class.isInstance(an2)){
                    flag = true;
                }
            }
        }
        String servletPath = request.getServletPath();
        logger.info("ApiRequestLimitInterceptor：请求数据 begin：servletPath:{}",servletPath );
        if(flag){
            // 把reqeust的body读取到StringBuilder
            BufferedReader reader = request.getReader();
            char[] buf = new char[1024];
            int rd;
            while((rd = reader.read(buf)) != -1){
                sb.append(buf, 0, rd);
            }
            if(!sb.toString().equals("{}")){
                JSONObject jsonObject = JSON.parseObject(sb.toString());
                if(jsonObject!=null){
                    String data = (String) jsonObject.get(MobileConfig.MOBILE_REQ_DATA_STR);
                    if(StringUtils.isNotBlank(data)){
                        ObjectMapper mapper = new ObjectMapper();
                        map = mapper.readValue(data, Map.class);
                    }
                }
            }
        }

        String mobileHeader = request.getHeader(MobileConfig.MOBILE_REQ_HEADER_STR);
        logger.info("服务器接受到的参数：mobileHeader:"+mobileHeader + "--- data:"+sb);
        ReqHeader reqHeader = JSON.parseObject(mobileHeader, ReqHeader.class);
        ReqVo reqVo = new ReqVo();
        if(map!=null){
            reqVo.setData(map);
        }
        reqVo.setHeader(reqHeader);
        ThreadLocalHandler.setThreadLocal("reqVo",reqVo);

        logger.info("ApiRequestLimitInterceptor：请求数据： mobileHeader:{},servletPath:{},data:{}",mobileHeader,servletPath ,sb);
        if(annotation!=null){
            logger.info("是否有注解：have mobileHeader:{},servletPath:{},data:{},annotation:{}",mobileHeader,servletPath ,sb,JSONObject.toJSONString(annotation));
            countValue = annotation.countValue();
            timeValue=annotation.timeValue();
            flag=annotation.isUseUserId();
        }else {
            logger.info("是否有注解：no have mobileHeader:{},servletPath:{},data:{}",mobileHeader,servletPath ,sb);
            return true;
        }
        String ipAddress = getIpAddress(request);
        String requestMethod = request.getMethod();
        String requestURI = request.getRequestURI();
        StringBuffer requestURL = request.getRequestURL();
        if(reqHeader!=null){
             cdid = reqHeader.getCDID();
        }

        String mobile = (String) reqVo.getValue("mobile");
        if(mobile == null){
            SessionInfo sessionInfo = (SessionInfo) mobileSession.getAttribute(MobileConfig.SESSION_USR_INFO);
            if(sessionInfo!=null){
                userId = sessionInfo.getUserId();
            }
        }
        key = (StringUtils.isNotBlank(mobile))?REQUEST_LIMIT_KEY+mobile+REQUEST_UNDERLINE+servletPath:REQUEST_LIMIT_KEY+userId+REQUEST_UNDERLINE+servletPath;
        logger.info("接口调用频率限制 开始：requestURI:{},requestURL:{},requestMethod:{},servletPath:{}",requestURI,requestURL,requestMethod,servletPath);
        logger.info("接口调用频率限制 开始：cdid:{},接口名称:{},调用Ip:{}",cdid,servletPath,ipAddress);
        logger.info("接口调用频率限制 开始：mobile:{},接口名称:{},调用Ip:{},userId:{}",mobile,servletPath,ipAddress,userId);
        logger.info("接口调用频率限制 开始：key:{}",key);

        List<Object> objects = redisUtil.lGet(key, 0, -1);
        if(objects ==null){
           List<Long> timeStamps = new ArrayList<>();
           timeStamps.add(System.currentTimeMillis());
           redisUtil.lSet(key,timeStamps);
        }else {
            Iterator<Object> iterator = objects.iterator();
            while (iterator.hasNext()){
                Long timeStamp  = (Long) iterator.next();
                long currentTimeMillis = System.currentTimeMillis();
                long diffValue = currentTimeMillis - timeStamp;
                if(diffValue>=timeValue){
                    redisUtil.lRemove(key,1,timeStamp);
                }
            }
            if(objects.size()>=countValue){
                throw new MobileException(MobileConfig.MOBILE_STATUS_REQUEST_LIMIT,MobileConfig.MOBILE_MSG_REQUEST_LIMIT);
            }

            redisUtil.lSet(key,System.currentTimeMillis());
        }
        logger.info("接口调用频率限制 结束：cdid:{},接口名称:{},调用Ip:{}",cdid,servletPath,ipAddress);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
    }

    /**
     * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * 参考文章：
     *
     * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
     * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
     *
     * 如：X-Forwarded-For：192.168.1.110, 192.168.1.120, 192.168.1.130,
     * 192.168.1.100
     *
     * 用户真实IP为： 192.168.1.110
     *
     * @param request
     * @return
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
